package main.grammar;

import java.util.ArrayList;
import java.util.List;

import view.model.GUINode;

public class Token implements Cloneable {

	private String name;
	private String value;
	private float probability;
	private int maximum;
	private int minimum;
	private boolean equiprobability;
	private boolean sequence;

	private List<Token> tokens = null;
	private GUINode guiNode;

	@Override
	public String toString() {
		return (name != null ? name + " " : "") + value;
	}

	public Token clone() {
		Token clone = new Token();
		clone.name = this.name;
		clone.value = this.value;
		clone.probability = this.probability;
		clone.maximum = this.maximum;
		clone.minimum = this.minimum;
		clone.equiprobability = this.equiprobability;
		clone.sequence = this.sequence;
		if (this.tokens != null) {
			clone.tokens = new ArrayList<Token>();
			for (Token token : this.tokens) {
				clone.tokens.add(token.clone());
			}
		}
		return clone;
	}

	public void addToken(Token token) {
		if (this.tokens == null) {
			this.tokens = new ArrayList<Token>();
		}
		if (this.sequence) {
			this.tokens.add(token);
		} else {
			boolean inserted = false;
			for (int i = 0; i < this.tokens.size() && !inserted; i++) {
				if (this.tokens.get(i).getProbability() > token.getProbability()) {
					this.tokens.add(i, token);
					inserted = true;
				}
			}
			if (!inserted) {
				this.tokens.add(token);
			}
		}
	}

	public void distribute() {
		if (tokens != null && tokens.size() > 0 && !sequence) {
			if (this.equiprobability) {
				tokens.get(0).setProbability(1.0f / this.tokens.size());
			}
			float sum = tokens.get(0).getProbability();
			for (int i = 1; i < this.tokens.size(); i++) {
				if (this.equiprobability) {
					sum += 1.0f / this.tokens.size();
				} else {
					sum += this.tokens.get(i).getProbability();
				}
				this.tokens.get(i).setProbability(sum);
			}
		}
	}

	public void unDistribute() {
		if (tokens != null && tokens.size() > 0 && !sequence) {
			float sum = tokens.get(0).getProbability();
			for (int i = 1; i < this.tokens.size(); i++) {
				sum -= this.tokens.get(i).getProbability();
				this.tokens.get(i).setProbability(sum);
			}
		}
	}

	public String generate() {
		String output = value != null ? value : "";
		int count = (int) (Math.random() * maximum) + minimum;
		for (int i = 0; i < count; i++) {
			float random = (float) Math.random();
			int bestProbabilityIndex = 0;
			if (tokens != null) {
				if (this.sequence) {
					for (Token token : tokens) {
						if (token.getProbability() >= random) {
							output += token.generate();
						}
					}
				} else {
					for (int j = 0; j < tokens.size(); j++) {
						if (tokens.get(j).probability >= random) {
							bestProbabilityIndex = j;
							break;
						}

					}
					output += tokens.get(bestProbabilityIndex).generate();
				}
			}
		}
		return output;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getValue() {
		return value;
	}

	public void setValue(String value) {
		this.value = value;
	}

	public float getProbability() {
		return probability;
	}

	public void setProbability(float probability) {
		this.probability = probability;
	}

	public int getMaximum() {
		return maximum;
	}

	public void setMaximum(int maximum) {
		this.maximum = maximum;
	}

	public int getMinimum() {
		return minimum;
	}

	public void setMinimum(int minimum) {
		this.minimum = minimum;
	}

	public List<Token> getTokens() {
		return tokens;
	}

	public void setTokens(List<Token> tokens) {
		this.tokens = tokens;
	}

	public boolean isEquiprobability() {
		return equiprobability;
	}

	public void setEquiprobability(boolean equiprobability) {
		this.equiprobability = equiprobability;
	}

	public boolean isSequence() {
		return sequence;
	}

	public void setSequence(boolean sequence) {
		this.sequence = sequence;
	}

	public GUINode getGuiNode() {
		return guiNode;
	}

	public void setGuiNode(GUINode guiNode) {
		this.guiNode = guiNode;
	}
	

}
